"""

    TESTS

"""

import sys

from tools import *
LOGPATH="/dee/DEE ZIK SET/pyLive/DEE/"
_OUT = Log( LOGPATH+"OUT.txt", out=True )
_ERR = Log( LOGPATH+"ERR.txt", err=True )
# use this to retore: _OUT.restore_sys()


if __name__ != "__main__":
    import Live
    sys.path.insert(
        0, '/dee/DEE ZIK SET/Installs/PYINST/Python 2.2.1/Lib/'
    )

# UPDATE THE DOCS
if 0:
    sys.path.insert(
        0, '/dee/DEE ZIK SET/pyLive/DEE/MY'
    )
    sys.path.insert(
        0, '/dee/DEE ZIK SET/pyLive/DEE/MY'
    )
    import docinsp
    if __name__ == "__main__":
        docinsp.GenDoc( "tools", LOGPATH+"Docs" )
    else:
        docinsp.GenDoc( "Live", LOGPATH+"Docs" )

#--- CONSTS
NOTE_OFF_STATUS = 128
NOTE_ON_STATUS = 144
CC_STATUS = 176


#--- DEVICE WRAPPER
class DeviceWrapper(object):
    def __init__( self, live ):
        print "Connecting."
        self.live = live
        self.LOG_FUNC( "test_logged_func" )
        #self.LOG_FUNC( "send_midi" )
        #self.LOG_FUNC( "build_midi_map" )
        #self.LOG_FUNC( "receive_midi" )
        #self.LOG_FUNC( "OnPlay" )
        print "Ok.\n"

        self._enableLoopLen = None
        self.hooked = False

    #--- TOOLS
    def _ARGS( self, name, *args, **kwargs ):
        """Utility to find out func signature."""
        print '>>', name, args, "KW:", kwargs

    def A( self, funcName, f ):
        def logged_func( *args, **kwargs ):
            print '[FUNC LOG]', funcName, 'ARGS:', args, "KW:", kwargs
            f( *args, **kwargs )
        return logged_func

    def LOG_FUNC( self, funcName ):
        """Decorator(like) to log func use."""
        f = getattr( self, funcName )
        logged_func = self.A(funcName,f)
        exec( "self."+funcName+" = logged_func" )
        print "LOGGING FUNC", funcName

    def test_logged_func( self, msg ):
        """Used to test the LOG_FUNC decorator."""
        print "<",msg,">"


    def show_message( self, *args ):
        """Show message to the live user. same nput as print."""
        self.live.show_message( ' '.join([self.str_or_repr(i) for i in args]) )
    
    def str_or_repr(self, o):
        if isinstance(o,(str,unicode)):
            return o
        return repr(o)
        
    #--- CONNECTION
    def disconnect(self):
        """Called when the script is removed."""
        print "Disconnecting."
        _OUT.restore_sys()
        _ERR.restore_sys()

    def connect_script_instances( self, instanciated_scripts ):
        """
        Called by the Application as soon as all scripts are initialized.
        You can connect yourself to other running scripts here, as we do it
        connect the extension modules
        """
        return

    #--- INSTALL CONFIG
    def is_extension(self):
        return False

    def can_lock_to_devices( self, *args, **kwargs ):
        return False

    def suggest_input_port( self, *args, **kwargs ):
        return ""

    def suggest_output_port( self, *args, **kwargs ):
        return ""

    #--- MIDI OPS
    def request_rebuild_midi_map(self):
        """
        To be called from any components, as soon as their internal state changed in a 
        way, that we do need to remap the mappings that are processed directly by the 
        Live engine.
        Dont assume that the request will immediately result in a call to
        your build_midi_map function. For performance reasons this is only
        called once per GUI frame.
        """
        return 

    def build_midi_map(self, midi_map_handle):
        """
        New MIDI mappings can only be set when the scripts 'build_midi_map' function 
        is invoked by our C instance sibling. Its either invoked when we have requested it 
        (see 'request_rebuild_midi_map' above) or when due to a change in Lives internal state,
        a rebuild is needed.
        """
        #channel, cc = 1, 49
        #map_mode = Live.MidiMap.MapMode.absolute
        #Live.MidiMap.forward_midi_cc( self.live.handle(), midi_map_handle, channel, cc )
        for channel in range(4):
            for cc in range(127):
                Live.MidiMap.forward_midi_cc(self.live.handle(), midi_map_handle, channel, cc)
            for note in range(127):
                Live.MidiMap.forward_midi_note(self.live.handle(), midi_map_handle, channel, note)


    def send_midi(self, midi_event_bytes):
        """
        Use this function to send MIDI events through Live to the _real_ MIDI devices 
        that this script is assigned to.
        """
        self.live.send_midi(midi_event_bytes)

    def receive_midi(self, midi_bytes):
        """Called by Live when midi is sent to the device."""
        status = midi_bytes[0] & 240
        channel = midi_bytes[0] & 15
        print "MIDI IN " + str(status) + " : " + str(channel), midi_bytes
        if (status == NOTE_ON_STATUS) or (status == NOTE_OFF_STATUS):
            note, velocity = midi_bytes[1:]
            print "  Note:", note, "Velocity:", velocity
            #self.dispatcher.handle_note(channel,note,velocity)
        elif (status == CC_STATUS):
            cc_no, cc_value = midi_bytes[1:]
            if cc_no == 49:
                print "Crossfader:", cc_value
            elif cc_no == 54:
                self.NextLoopLen( cc_value )
            elif cc_no == 10:
                self.ToggleLoopLen( cc_value )
            else:
                #print "  CC:", cc_no, "Value:", cc_value
                pass
                
    def suggest_map_mode(self, cc_no):
        """
        Give a hint on the midi map to use on cc_no.
        Return something like:
            Live.MidiMap.MapMode.absolute
            Live.MidiMap.MapMode.relative_signed_bit
            ...
        """
        return Live.MidiMap.MapMode.absolute

    #--- DOC ACCESS
    def song(self):
        return Live.Application.get_application().get_document()

    #--- LOOP
    def refresh_state( self, *args, **kwargs ):
        return

    def update_display( self, *args, **kwargs ):
        """Called every 100ms"""
        if 0 and not self.hooked: #######################################################<<<<<
            song = self.song()
            if song is None:
                return
            self.hooked = True
            print "hooking to song:", song
            song.add_is_playing_listener( self.OnPlay )
        self.OnPlay()

    def OnPlay(self, *args, **kwargs ):
        """add_is_playing_listener"""
        self.send_midi((176, 8, 127*self.song().is_playing))

    def get_clip(self):
        song = self.song()
        if song is None:
            return None
        for clipSlot in song.tracks[0].clip_slots:
            if clipSlot.clip is not None:
                return clipSlot.clip
        return None
        
    def ToggleLoopLen(self, on ):
        if on != 0:
            # store state
            clip = self.get_clip()
            if clip is None:
                self._enableLoopLen = None
                return
            self._enableLoopLen = (
               clip, clip.loop_start, clip.loop_end
            )
            self.show_message("stored", self._enableLoopLen, clip.length)
            return
        else:
            # back to previous
            if self._enableLoopLen is not None:
                clip, start, end = self._enableLoopLen
                clip = self.get_clip()
                self.show_message("try", self._enableLoopLen, clip.length)
                self._enableLoopLen = None
                clip.loop_start = start
                clip.loop_end = end
                self.show_message("ok restored", self._enableLoopLen, clip.length)
            
    def NextLoopLen( self, value ):
        if self._enableLoopLen is None:
            return
        # loop_start must be >0 and <loop_end
        clip = self.get_clip()
        s = clip.loop_end
        ok = []
        bad = []
        nb=50
        for i in range(nb):
            v = (-nb/2+i)/5.0
            try:
                clip.loop_end = v
            except:
                bad.append(v)
            else:
                bad.append([v])
                ok.append(v)
        self.show_message(s,ok,bad)
        return
        
        clip.loop_end = clip.length / 2.0
        return
        if value < 2:
            s = clip.loop_start
            l = clip.length
            clip.loop_start = clip.length/2.0
            clip.loop_length = l
            self.show_message( s,"+>",clip.loop_start,clip.loop_end,clip.length)
        else:
            s = clip.loop_start
            l = clip.length
            clip.loop_start = clip.loop_start - clip.length/2.0
            clip.loop_length = l
            self.show_message( s,"->",clip.loop_start,clip.loop_end,clip.length)
        return
        
        #qtz = song.clip_trigger_quantization
        #qtzLen = None
        #for l, q in Live.Song.Quantization.values.items():
        #    if q == qtz:
        #        qtzLen = l
        #        break
        #print qtz, qtzLen
        #print song.tracks[0].name
        for clipSlot in song.tracks[0].clip_slots:
            if clipSlot.clip is not None:
                clip = clipSlot.clip
                start = clip.loop_start
                end = clip.loop_end
                loopLen = end - start
                print start, end, loopLen
                if 1 or loopLen == qtzLen:
                    new_end = start + loopLen/2.0
                else:
                    new_end = start + qtzLen
                print ">>>",new_end
                clip.loop_end = new_end



#--- MAIN
def create_instance(c_instance):
        return DeviceWrapper( c_instance )




if __name__ == "__main__":
    print "Test"
    lw = create_instance( None )
    lw. test_logged_func( "toto" )
    print "Ok."


